package concurrecy;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Waiter2 implements Runnable {
    private Restaurant2 restaurant;

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public Waiter2(Restaurant2 restaurant) {
        this.restaurant = restaurant;
    }

    public void signal() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                lock.lockInterruptibly();
                try {
                    while (restaurant.getMeal() == null) { // 菜还没准备好，等厨师做菜
                        System.out.println("waiter waiting ... ");
                        condition.await();
                    }
                } finally {
                    lock.unlock();
                }

                Meal meal = restaurant.getMeal();
                System.out.println("take " + meal);
                synchronized (restaurant.getChef()) {
                    restaurant.setMeal(null); // 把菜端走了
                    restaurant.getChef().signal(); // 催厨师做菜
                }
            }
        } catch(InterruptedException e) {
            System.out.println("waiter interrupted");
        }
        System.out.println("waiter go home");
    }
}

class Chef2 implements Runnable {
    private Restaurant2 restaurant;
    private int count = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public Chef2(Restaurant2 restaurant) {
        this.restaurant = restaurant;
    }

    public void signal() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                lock.lockInterruptibly();
                try {
                    while (restaurant.getMeal() != null) { // 食物还没有被端走，等服务员端走
                        System.out.println("chef waiting ... ");
                        condition.await();
                    }
                } finally {
                    lock.unlock();
                }
                Meal meal = new Meal(count++);
                System.out.println("make " + meal);
                synchronized (restaurant.getWaiter()) {
                    restaurant.setMeal(meal); // 生产好了食物
                    restaurant.getWaiter().signal(); // 叫服务员上菜
                }

                if (count > 10)
                    restaurant.closeDoor(); // 食物卖完了，关门
                TimeUnit.MILLISECONDS.sleep(500); // 休息一下
            }
        } catch(InterruptedException e) {
            System.out.println("chef interrupted");
        }
        System.out.println("chef go home");
    }
}

class Restaurant2 {
    private Chef2 chef = new Chef2(this);
    private Waiter2 waiter = new Waiter2(this);
    private volatile Meal meal;

    private ExecutorService executorService = Executors.newCachedThreadPool();

    public Restaurant2() {
        executorService.execute(chef);
        executorService.execute(waiter);
    }

    /**
     * 关门，打烊
     */
    public void closeDoor() {
        System.out.println("closeDoor");
        executorService.shutdownNow();
    }

    public Chef2 getChef() {
        return chef;
    }

    public Waiter2 getWaiter() {
        return waiter;
    }

    public Meal getMeal() {
        return meal;
    }

    public void setMeal(Meal meal) {
        this.meal = meal;
    }
}


/**
 * 餐厅 （生产者-消费者）
 */
public class RestaurantDemo2 {
    public static void main(String[] args) {
        new Restaurant2();
    }
}
